package org.usmile.algorithms.leetcode.hard;

/**
 * 164. 最大间距
 * 
 * 给定一个无序的数组 nums，返回 数组在排序之后，相邻元素之间最大的差值 。如果数组元素个数小于 2，则返回 0 。
 * 您必须编写一个在「线性时间」内运行并使用「线性额外空间」的算法。
 * 
 * 示例 1:
 * 输入: nums = [3,6,9,1]
 * 输出: 3
 * 解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。
 * 
 * 示例 2:
 * 输入: nums = [10]
 * 输出: 0
 * 解释: 数组元素个数小于 2，因此返回 0。
 *
 * 提示:
 * 1 <= nums.length <= 105
 * 0 <= nums[i] <= 109
 */
public class _0164 {
    public static void main(String[] args) {
//        int[] nums = new int[]{2,6,9,1};
        int[] nums = new int[]{1, 1, 1, 1};
        System.out.println(new _0164_Solution2().maximumGap(nums));
    }
}

class _0164_Solution1 {
    public int maximumGap(int[] nums) {
        if (nums == null || nums.length <= 1) {
            return 0;
        }

        int max = nums[0];
        for (int num : nums) {
            max = Math.max(max, num);
        }

        for (int exp = 1; max / exp > 0; exp *= 10) {
            countSort(nums, exp);
        }

        int maxGap = 0;
        for (int i = 1; i < nums.length; i++) {
            maxGap = Math.max(maxGap, nums[i] - nums[i - 1]);
        }

        return maxGap;
    }

    @SuppressWarnings("all")
    private void countSort(int[] nums, int exp) {
        int[] count = new int[10];
        for (int num : nums) {
            int digit = (num / exp) % 10;

            count[digit]++;
        }

        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }

        int[] sortResult = new int[nums.length];
        for (int i = nums.length - 1; i >= 0; i--) {
            int digit = (nums[i] / exp) % 10;
            sortResult[count[digit]-- - 1] = nums[i];
        }

        for (int i = 0; i < nums.length; i++) {
            nums[i] = sortResult[i];
        }
    }
}

@SuppressWarnings("all")
class _0164_Solution2 {
    public int maximumGap(int[] nums) {
        if (nums == null || nums.length <= 1) {
            return 0;
        }

        int min = nums[0];
        int max = nums[0];
        for (int num : nums) {
            min = Math.min(min, num);
            max = Math.max(max, num);
        }
        if (max == min) {
            return 0;
        }

        int gap = (int) Math.ceil((double) (max - min) / (nums.length - 1));
        int[][] buckets = new int[(max - min) / gap + 1][];
        for (int num : nums) {
            int bucketIndex = (num - min) / gap;
            if (buckets[bucketIndex] == null) {
                int[] bucket = new int[]{num, num};
                buckets[bucketIndex] = bucket;
            } else {
                buckets[bucketIndex][0] = Math.min(buckets[bucketIndex][0], num);
                buckets[bucketIndex][1] = Math.max(buckets[bucketIndex][1], num);
            }
        }

        int maxGap = 0;
        int prev = min;
        for (int i = 0; i < buckets.length; i++) {
            if (buckets[i] != null) {
                maxGap = Math.max(maxGap, buckets[i][0] - prev);
                prev = buckets[i][1];
            }
        }

        return maxGap;
    }
}